#pragma once
#define _USE_MATH_DEFINES
#include <string>
#include <iostream>
#include <complex>
#include <math.h>

#include "targetver.h"
#include <windows.h>

#include <stdio.h>
#include <tchar.h>
#include <time.h>
#include <vector>
#include <queue>
#include <string>

#include <mpi.h>

using namespace std;

// Short dwarf's description.
#define DWARF_NAME (char*)"Graph Models, unmanaged mpi kernel."

// Constants for output files's names
#define DWARF_NAME_OUTPUT (char*)"Dwarf.Unmanaged.Mpi"
#define NUMBER_ROOT_PROCESS 0                       // index of root process.
#define PROFILE_TXT (char*)".Profile.txt"
#define RESULT_TXT (char*)".Result.txt"

// Default size for buffer.
#define BUFFER_SIZE 1024

//Parse the command line arguments and fill the reference parameters.
typedef int (__cdecl *DLLParseArguments)(
    int,                                    //Count of command line tokens.
    char**,                                 //Command line tokens.
    char**,                                 //Name of input text file.
    char**,                                 //Name of profile text file.
    char** );                               //Name of result file.

// Write the rules of command line structure.
typedef void (__cdecl *DLLUsage)(void);

// Start the time count.
typedef void (__cdecl *DLLStart)(void);

// Stop the time count.
typedef void (__cdecl *DLLStop)(double*);

//Presentation of Hidden Markov model
class HMM 
{
private:
    int numberOfStates;                         // number of states;  Q = {1,2,...,N} 
    int numberOfObservationSymbols;             // number of observation symbols; V={1,2,...,M}
public:
    double **stateTransitionMatrix;             // A[1..N][1..N]. a[i][j] is the transition prob
                                                // of going from state i at time t to state j
                                                // at time t+1 

    double  **observationProbabilityMatrix;     // B[1..N][1..M] is the  probability 
                                                // of observing symbol j in state i 

    double *initialStates;                      // initialStates[1..numberOfStates]  is the initial state distribution. 

    HMM(int n, int m)
    {
        numberOfStates = n;
        numberOfObservationSymbols = m;
        //creation of  state transition and observation matrices
        stateTransitionMatrix = new double*[numberOfStates];
        observationProbabilityMatrix = new double*[numberOfStates];
        for(int i = 0; i < numberOfStates; i++)
        {
            stateTransitionMatrix[i] = new double[numberOfStates];
            observationProbabilityMatrix[i] = new double[numberOfObservationSymbols];
        }

        initialStates = new double[numberOfStates];
        //initialization
        for(int i=0; i < numberOfStates; i++ )
        {
            for(int j=0; j < numberOfStates; j++)
            {
                stateTransitionMatrix[i][j] = 0;
            }
            for(int j=0; j < numberOfObservationSymbols; j++)
            {
                observationProbabilityMatrix[i][j] = 0;
            }
            initialStates[i] = 0;
        }
    };

    ~HMM()
    {
        for(int i = 0;i < numberOfStates; i++)
        {
            delete[]stateTransitionMatrix[i];
            delete[]observationProbabilityMatrix[i];
        }
        delete []stateTransitionMatrix;
        delete []observationProbabilityMatrix;
        delete []initialStates;
    };

    int getNumberOfStates() 
    { 
        return numberOfStates;
    }

    int getNumberOfObservation() 
    {
        return numberOfObservationSymbols;
    }
};

//Presentation of Viterbi model
class ViterbiModel
{
private:
    int lengthOfObservationSequence;            //length of the sequence
    double probability;                         //the maximization of probability
public:
    double **delta;                             // is the best score(highest probability) along a single path,at time t
    int **psi;                                  //track of the argument which maximized
    int *observationSequence;                   // the observation sequence [ O ]
    int *sequenceOfState;                       // the sequence of state [ Q ] 

    ViterbiModel(int t,HMM *hmm)
    {
        lengthOfObservationSequence = t;
        //creation of delta and psi work elements
        delta = new double*[lengthOfObservationSequence];
        psi = new int*[lengthOfObservationSequence];
        int numberOfState = hmm->getNumberOfStates();
        for(int i = 0; i < lengthOfObservationSequence; i++)
        {
            delta[i] = new double[numberOfState];
            psi[i] = new int[numberOfState];
        }

        observationSequence = new int[lengthOfObservationSequence];
        sequenceOfState = new int[lengthOfObservationSequence];

        //initialization
        for(int i = 0; i < lengthOfObservationSequence; i ++)
        {
            for(int j = 0; j < numberOfState; j++)
            {
                delta[i][j] = 0;
                psi[i][j] = 0;
            }
            observationSequence[i] = 0;
            sequenceOfState[i] = 0;
        }


    };

    ~ViterbiModel()
    {
        for(int i = 0;i < lengthOfObservationSequence; i++)
        {
            delete[]psi[i];
            delete[]delta[i];
        }
        delete []psi;
        delete []delta;
        delete []observationSequence;
        delete []sequenceOfState;
    };

    int getLengthOfObservationSequence() 
    {
        return lengthOfObservationSequence;
    }

    double getProbability()
    {
        return probability;
    }

    void setProbability(double p)
    { 
        probability = p;
    }
};


class Solver
{
public:
    int commSize;                   // Rank of the process.
    int commRank;                   // Count of processes.

    bool isRootThread;              //true for master process
    int lengthForProc;              //the loading of process
    int *arrayOfProccLengths;       //the array of loading of all processes
    int *arrayOfShifts;             //the array of shifts of all processes
    int startIndx;                  
    int endIndx;


    void init(int n, int m, int t);
    HMM *hmm;                       //Presentation of Hidden Markov model
    ViterbiModel *vit;              //Presentation of Viterbi model
    Solver();
    ~Solver();
    void solve();
};

// Settings taken from command line and input file.
class Settings
{
public:
    Settings();
    ~Settings();

    void finish(Solver *solver);
    void start();
    int init(int argc, char** argv, Solver *solver);

private:
    // Input & output files.
    char* inputFile;
    char* profileFile;
    char* resultFile;

    int parseConfigFile(Solver* solver);    // Parse input file into solver's structures.
    void writeSettings();                   // Write settings.
};